<script setup lang="ts">
import { onMounted, ref, useSlots, watch } from 'vue';
import type { UploadCustomRequestOptions, UploadFileInfo, UploadInst } from 'naive-ui';
import { alova } from '@/service/request';
import { randomCode } from '@/utils/common';

const slots = useSlots();

defineOptions({
  name: 'PorUpload'
});

interface Props {
  action?: string;
  headers?: Record<string, string>;
  data?: Record<string, any>;
  value?: string[] | string;
}

const props = withDefaults(defineProps<Props>(), {
  action: '',
  headers: () => ({}),
  data: () => ({}),
  fileList: () => [],
  value: ''
});

const emit = defineEmits<{
  /** 绑定的数据 */
  'update:value': [value: string[] | string];
}>();

const uploadRef = ref<UploadInst | null>(null);
const fileList = ref<UploadFileInfo[]>([]);
const isLoading = ref(false);

watch(
  () => props.value,
  () => {
    syncFileList();
  }
);

function syncFileList() {
  const value = props.value;
  fileList.value = [];
  if (typeof value === 'string') {
    fileList.value.push({
      id: randomCode(8),
      name: value.substring(value.lastIndexOf('/') + 1),
      status: 'finished',
      url: value
    });
  } else if (Array.isArray(value)) {
    fileList.value = (value as string[]).map(url => {
      return {
        id: randomCode(8),
        name: url.substring(url.lastIndexOf('/') + 1),
        status: 'finished',
        url
      };
    });
  }
}

async function toUpload({ file, onFinish, onError }: UploadCustomRequestOptions) {
  if (!file.file) return;
  isLoading.value = true;

  try {
    const formData = new FormData();
    formData.append('file', file.file);

    // Add additional data if provided
    Object.entries(props.data).forEach(([key, value]) => {
      formData.append(key, value);
    });

    // Add additional data if provided
    const storageFile = await alova
      .Post<Api.Model>(props.action, formData, {
        headers: { ...props.headers }
      })
      .send();

    file.url = storageFile.url;

    onFinish();

    emit('update:value', storageFile.url);
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
  } catch (_error) {
    onError();
  } finally {
    isLoading.value = false;
  }
}

const uploadFiles = async () => {
  if (!uploadRef.value || fileList.value.length === 0) {
    return;
  }

  isLoading.value = true;

  try {
    const formData = new FormData();
    fileList.value.forEach(f => {
      if (f.file) {
        formData.append('file', f.file);
      }
    });

    // Add additional data if provided
    Object.entries(props.data).forEach(([key, value]) => {
      formData.append(key, value);
    });

    // Add additional data if provided
    const storageFiles = await alova
      .Post<Api.Model[]>(props.action, formData, {
        headers: { ...props.headers }
      })
      .send();

    const urls = storageFiles.map(item => item.url);

    emit('update:value', urls as string[]);
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
  } catch (_error) {
  } finally {
    isLoading.value = false;
  }
};

defineExpose({
  uploadFiles
});

onMounted(() => {
  if (props.value) {
    syncFileList();
  }
});
</script>

<template>
  <NUpload
    ref="uploadRef"
    v-model:file-list="fileList"
    :action="action"
    :data="data"
    :headers="headers"
    :custom-request="toUpload"
  >
    <template v-for="name in Object.keys(slots)" #[name]="slotData">
      <slot :name="name" v-bind="slotData || {}" />
    </template>
  </NUpload>
</template>

<style scoped></style>
